package com.guli.auth.controller;

import com.alibaba.fastjson.JSON;
import com.guli.auth.service.LoginService;
import com.guli.common.constant.AuthServerConstant;
import com.guli.common.constant.WebSiteConstant;
import com.guli.common.to.MemberTo;
import com.guli.auth.vo.UserVo;
import com.guli.common.utils.R;
import com.guli.common.valid.LoginGroup;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/*
 * @Author 罗
 * @date 2020/10/19 - 8:30 上午
 */
@Controller
@RequestMapping("/login")
@Slf4j
public class LoginController {

    @Resource
    LoginService loginService;

    @Resource
    StringRedisTemplate stringRedisTemplate;

    @GetMapping("/preLogin")
    @ApiOperation("预登陆，如果包含cookie，就直接跳转登陆后的最终地址")
    public String preLoginByCookie(@RequestParam(value = AuthServerConstant.FINAL_URL_AFTER_LOGIN, required = false) String finalUrl,
                                   @CookieValue(value = AuthServerConstant.SSO_AUTH_SERVER_COOKIE, required = false) String cookieFromBrowser
    ) {
        if (!ObjectUtils.isEmpty(finalUrl) && !ObjectUtils.isEmpty(cookieFromBrowser)) {

            String token = stringRedisTemplate.opsForValue().get(cookieFromBrowser);

            if (!ObjectUtils.isEmpty(token)) {
                return String.format("redirect:%s?token=%s", finalUrl, cookieFromBrowser);
            }
        }

        return String.format("redirect:%s?finalUrl=%s", WebSiteConstant.AUTH_WEB_PAGE, finalUrl);
    }


    @ResponseBody
    @PostMapping("loginByUserNameAndPassword")
    public R loginByUserNameAndPassword(@RequestBody @Validated(LoginGroup.class) UserVo userVo,
                                        @RequestParam(value = AuthServerConstant.FINAL_URL_AFTER_LOGIN, required = false) String finalUrl,
                                        @CookieValue(value = AuthServerConstant.SSO_AUTH_SERVER_COOKIE, required = false) String cookieFromBrowser,
                                        HttpServletRequest request,
                                        HttpSession session,
                                        HttpServletResponse response) {

        /*
        // cookie 存到 Spring-session 中时，使用这个
        Object cookieToken = session.getAttribute(AuthServerConstant.SSO_AUTH_SERVER_COOKIE);

        if (!ObjectUtils.isEmpty(cookieToken)) {
            // 用户之前登陆过
            log.info("该用户有 cookieToken = {}，用户之前已经登陆过",cookieToken);
            return R.ok().put("token", cookieToken);
        }*/
        if (!ObjectUtils.isEmpty(cookieFromBrowser)) {
            String token = stringRedisTemplate.opsForValue().get(cookieFromBrowser);
            if (!ObjectUtils.isEmpty(token)) {

                log.info("用户访问时，自带了已经认证过的cookie={}，用户之前已经登陆过，这是留下的登陆痕迹", cookieFromBrowser);
                return R.ok().put("token", cookieFromBrowser);
            }
            log.warn("用户虽然之前登陆过token={}，但是过期或者失效了", cookieFromBrowser);
        }

        MemberTo memberTo = loginService.loginByUserNameAndPassword(userVo);

        // 用于认证自己人
        session.setAttribute(AuthServerConstant.LOGIN_USER_SESSION, memberTo);

        R r = R.ok().put("user", memberTo);

        /**
         * 如果有 finalUrl 说明是其他域名的服务请求登陆
         * 将用户的登陆信息放在redis中，以 uuid 为 token，uuid也为用户信息的key
         * */
        if (!ObjectUtils.isEmpty(finalUrl)) {

            String token = UUID.randomUUID().toString();

            // 将登陆信息放在 redis
            stringRedisTemplate.opsForValue().set(token, JSON.toJSONString(memberTo), 30, TimeUnit.MINUTES);

            /**
             * 给浏览器端也保存一个cookie，下次三方应用请求登陆时（跳转此认证服务的前端登陆页面），就会携带此token-cookie，用于标记用户已经登陆过
             * */
            Cookie cookie = new Cookie(AuthServerConstant.SSO_AUTH_SERVER_COOKIE, token);

//            session.setAttribute(AuthServerConstant.SSO_AUTH_SERVER_COOKIE,token); // 而 这样保存是将 cookie 保存到 spring-session-redis 中，也能实现一些效果

            /**
             * 注意要配合 @CookieValue 注解才能从请求中获取到 cookie
             * */
            response.addCookie(cookie); // addCookie() 只会在浏览器端保存数据，spirng-session 不将其保存到redis

            r.put("token", token);
        }
        return r;
    }

    //@ResponseBody
    @GetMapping("/loginByWeiBo")
    public String loginByWeiBo(@RequestParam("code") String code, HttpServletRequest request, HttpServletResponse response/*, ModelAndView modelAndView*/) throws IOException {

        log.info("接收到微博的第一个令牌：{}", code);

        //2、拿着微博返回的第一个令牌去兑换第二个令牌（accessToken）
        MemberTo memberTo = loginService.getAccessTokenByToken(code);

        Cookie cookie = new Cookie("hello", "你好");
        cookie.setDomain("guli.com"); // 默认使用进入此服务时使用的域名，即 auth.guli.com
        /**
         * 第一次使用 session 就命令浏览器保存卡号。JSESSIONID这个cookie
         * 以后浏览器访问哪个网站就会带上这个网站的cookie
         * 子域名之间 guli.com,search.guli.com,item.guli.com,auth.guli.com
         * 发卡的时候（指定域名为父域名（guli.com），即使是子域发送的卡，也能让父域直接使用）
         * 即使是有 @ResponseBody 注解的前后端分离项目，也能存储cookie到浏览器
         * */
        response.addCookie(cookie);

        //TODO 默认 spring-session 发的令牌只能在当前子域名下有效，父域或其他域无法使用
        //TODO 2 使用json来序列化对象
        request.getSession().setAttribute(AuthServerConstant.LOGIN_USER_SESSION, memberTo); //放入时，对象需要实现 Serializable 接口

        //return "redirect:http://guli.com/index.html"; //

        //return R.ok().put("user", memberTo);
        return "redirect:http://guli.com/index.html";
    }

}
